from abc import ABC, abstractmethod

from bcat.gtp.serde.error import ExpectTypeError, ExpectNumberLengthError

class Writer(ABC):
    def __init__(self, target):
        self.__target = target

    @abstractmethod
    def discard_back(self, n):
        pass

    def target(self):
        return self.__target
    def set_target(self, value):
        self.__target = value

    @abstractmethod
    def clear(self):
        pass

    @abstractmethod
    def _write_bytes(self, bs):
        return 0

    def write_bytes(self, bs):
        if type(bs) in (bytes, bytearray, memoryview):
            return self._write_bytes(bs)
        else:
            raise ExpectTypeError.from_real_value((bytes, bytearray, memoryview), bs)
    
    def write_str(self, s, encoding="utf-8"):
        if type(s) is str: 
            return self._write_bytes(s.encode(encoding))
        else:
            raise ExpectTypeError.from_real_value(str, s)

    def write_uint8(self, v):
        return self.write_uint(v, 1)
    def write_uint16(self, v):
        return self.write_uint(v, 2)
    def write_uint32(self, v):
        return self.write_uint(v, 4)
    def write_uint64(self, v):
        return self.write_uint(v, 8)

    def write_int8(self, v):
        return self.write_int(v, 1)
    def write_int16(self, v):
        return self.write_int(v, 2)
    def write_int32(self, v):
        return self.write_int(v, 4)
    def write_int64(self, v):
        return self.write_int(v, 8)
        
    def write_uint(self, u, expect_byte_len):
        return self._write_integer(u, expect_byte_len, int(u.bit_length()/8))

    def write_int(self, i, expect_byte_len):
        return self._write_integer(i, expect_byte_len, int(i.bit_length()/8 + 1))

    def _write_integer(self, n, expect_byte_len, real_byte_len):
        if type(n) is int and real_byte_len <= expect_byte_len:
            return self._write_bytes(n.to_bytes(expect_byte_len, 'little'))
        elif type(n) is not int:
            raise ExpectTypeError.from_real_value(int, n)
        else: #n.bit_length() > bit_length:
            raise ExpectNumberLengthError.from_real_len(expect_byte_len, real_byte_len)
        

    def write_float(self, v):
        #TODO:  struct.pack
        return 0
        """
        if type(v) is float:
            return self._write_str(str(v))
        elif type(v) is not float:
            raise ExpectTypeError.from_real_value(float, v)
        """

    def write_bool(self, b):
        if type(b) is bool:
            return self.write_bytes(b'\x01' if b else b'\x00')
        else:
            raise ExpectTypeError.from_real_value(bool, b)
        
